package com.zmn.uac.business.impl.oauth.login;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.nacos.api.config.annotation.NacosValue;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiServiceGetCorpTokenRequest;
import com.dingtalk.api.request.OapiV2UserGetRequest;
import com.dingtalk.api.request.OapiV2UserGetuserinfoRequest;
import com.dingtalk.api.response.OapiServiceGetCorpTokenResponse;
import com.dingtalk.api.response.OapiV2UserGetResponse;
import com.dingtalk.api.response.OapiV2UserGetuserinfoResponse;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.taobao.api.ApiException;
import com.zmn.common.dto2.ResponseDTO;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.saas.dubbo.dto.app.AppDRO;
import com.zmn.uac.business.interfaces.oauth.login.DingTalkLoginBService;
import com.zmn.uac.business.template.oauth.login.BaseLoginTemplateMode;
import com.zmn.uac.common.constant.AccessTypeConstant;
import com.zmn.uac.common.constant.DingTalkConstant;
import com.zmn.uac.common.constant.RedisKeyConstant;
import com.zmn.uac.common.dio.oauth.login.TempCodeDIO;
import com.zmn.uac.common.dio.oauth.login.UserAuthLoginDIO;
import com.zmn.uac.common.entity.oauth.login.*;
import com.zmn.uac.common.entity.oauth.phone.UserPhoneInfo;
import com.zmn.uac.common.info.UserAuthPhoneInfo;
import com.zmn.uac.common.info.dingtalk.DingTalkUserDetailedInfo;
import com.zmn.uac.common.info.dingtalk.DingTalkUserInfo;
import com.zmn.uac.common.info.dingtalk.DingTalkUserInformation;
import com.zmn.uac.util.HttpClientsUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 类描述：钉钉登陆业务实现
 *
 * @author xujie
 * @since 2021/05/19 16:07
 */
@Slf4j
@Service
public class DingTalkLoginBServiceImpl extends BaseLoginTemplateMode implements DingTalkLoginBService {

    private static final String TAG = "钉钉登陆业务实现";

    @NacosValue(value = "${ding.talk.suite.key}", autoRefreshed = true)
    String suiteKey;

    @NacosValue(value = "${ding.talk.suite.secret}", autoRefreshed = true)
    String suiteSecret;

    @NacosValue(value = "${ding.talk.auth.corp.id}", autoRefreshed = true)
    String authCorpId;

    @NacosValue(value = "${ding.talk.token}", autoRefreshed = true)
    String token;

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public ResponseDTO<UserAuthLoginInfo> thirdPartyUserDataAcquisition(UserAuthLoginDIO dio, UserAuthPhoneInfo authPhoneInfo) {
        String uuid = dio.getUuid();
        ResponseDTO<DingTalkUserInformation> response = getUserInfoByCode(uuid, dio.getCode());
        if (!response.isSuccess()) {
            return ResponseDTO.fail(response.getMessage());
        }
        DingTalkUserInformation user = response.getData();
        DingTalkUserInfo userInfo = new DingTalkUserInfo();
        userInfo.setNick(user.getName());
        userInfo.setOpenId(user.getUserId());
        userInfo.setUnionId(user.getUnionId());
        userInfo.setMainOrgAuthHighLevel(false);
        ResponseDTO<DingTalkUserDetailedInfo> userDetailedInfoResponseDTO = getUserDetailedInfoByUserId(uuid, user.getUserId());
        if (userDetailedInfoResponseDTO.isSuccess()) {
            DingTalkUserDetailedInfo userDetailedInfo = userDetailedInfoResponseDTO.getData();
            userInfo.setAvatar(userDetailedInfo.getAvatar());
        }
        Integer channelId = dio.getChannelId();
        AppDRO app = Objects.equals(dio.getAccessType(), AccessTypeConstant.SHOP) ? getAppInfoByShopId(dio.getShopId()) : getAppByChannelId(channelId);
        if (Objects.isNull(app)) {
            return ResponseDTO.fail("查询应用为null");
        }
        UserInfo info = setUserInfo(userInfo, dio);
        PlatInfo platInfo = setPlatInfo(dio, app, channelId);
        ThirdAppInfo thirdAppInfo = setThirdAppInfo(app);
        UserAuthLoginInfo userAuthLoginInfo = setUserAuthLoginInfo(info, platInfo, thirdAppInfo, authPhoneInfo);

        return ResponseDTO.success(userAuthLoginInfo);
    }

    @Override
    public ResponseDTO<String> getThirdPartyOpenIdByCode(TempCodeDIO tempCodeDIO, AppDRO app) {
        ResponseDTO<DingTalkUserInformation> response = getUserInfoByCode(null, tempCodeDIO.getCode());
        if (!response.isSuccess()) {
            return ResponseDTO.fail(response.getMessage());
        }
        log.info("获取钉钉调用响应DingTalkUserInformation:{}", response.getData());
        return ResponseDTO.success(response.getData().getUserId());
    }

    @Override
    public ResponseDTO<String> getDingTalkAccessToken() {
        String accessToken = redisTemplate.opsForValue().get(RedisKeyConstant.UAC_DING_TALK_ACCESS_TOKEN);
        if (StringUtil.isNotBlank(accessToken)) {
            return ResponseDTO.success(accessToken);
        }
        DefaultDingTalkClient client = new DefaultDingTalkClient(DingTalkConstant.DING_TALK_GET_TOKEN_URL);
        OapiServiceGetCorpTokenRequest request = new OapiServiceGetCorpTokenRequest();
        request.setAuthCorpid(authCorpId);
        OapiServiceGetCorpTokenResponse response;
        try {
            String suiteTicket = null;
            if (DingTalkConstant.TOKEN.equals(token)) {
                suiteTicket = redisTemplate.opsForValue().get(RedisKeyConstant.UAC_DING_TALK_CALLBACK_SUITE_TICKET);
            } else {
                String httpResult =  HttpClientsUtil.executeGet(DingTalkConstant.PROD_SUITE_TICKET_URL);
                ResponseDTO<String> httpReResponseDTO = new Gson().fromJson(new Gson().toJson(JSONObject.parseObject(httpResult, ResponseDTO.class)), new TypeToken<ResponseDTO<String>>() {
                }.getType());
                if (httpReResponseDTO.isSuccess()) {
                    suiteTicket = httpReResponseDTO.getData();
                } else {
                    log.error("[{}], 请求线上的suiteTicket失败", TAG);
                }
            }
            if (StringUtil.isBlank(suiteTicket)) {
                log.error("[{}], 获取钉钉推送suiteTicket为null", TAG);
                return ResponseDTO.fail("获取钉钉SuiteTicket为null");
            }
            response = client.execute(request, suiteKey, suiteSecret, suiteTicket);
            accessToken = response.getAccessToken();
            redisTemplate.opsForValue().set(RedisKeyConstant.UAC_DING_TALK_ACCESS_TOKEN, accessToken, 2 * 60 - 1, TimeUnit.MINUTES);
            return ResponseDTO.success(accessToken);
        } catch (ApiException e) {
            log.error("[{}], 获取钉钉AccessToken异常:{}", TAG, e.getErrMsg(), e);
        }
        return ResponseDTO.fail("获取钉钉AccessToken异常");
    }

    private ResponseDTO<DingTalkUserInformation> getUserInfoByCode(String uuid, String requestAuthCode) {
        ResponseDTO<String> responseDTO = getDingTalkAccessToken();
        if (!responseDTO.isSuccess()) {
            return ResponseDTO.fail(responseDTO.getMessage());
        }
        String accessToken = responseDTO.getData();
        DingTalkClient client = new DefaultDingTalkClient(DingTalkConstant.GET_USER_INFO);
        OapiV2UserGetuserinfoRequest req = new OapiV2UserGetuserinfoRequest();
        req.setCode(requestAuthCode);
        OapiV2UserGetuserinfoResponse rsp;
        try {
            rsp = client.execute(req, accessToken);
            log.info("[{}], {}, 钉钉获取用户信息:{}", TAG, uuid, rsp.getBody());
            if (rsp.getErrcode() == 0) {
                JSONObject body = JSON.parseObject(rsp.getBody());
                JSONObject result = body.getJSONObject("result");
                DingTalkUserInformation information = JSONObject.parseObject(result.toJSONString(), DingTalkUserInformation.class);
                return ResponseDTO.success(information);
            } else {
                return ResponseDTO.fail(rsp.getMessage());
            }
        } catch (ApiException e) {
            log.error("[{}], {}, 钉钉获取用户信息异常:{}", TAG, uuid, e.getErrMsg(), e);
            return ResponseDTO.fail("获取用户信息失败！");
        }
    }

    private ResponseDTO<DingTalkUserDetailedInfo> getUserDetailedInfoByUserId(String uuid, String userId) {
        ResponseDTO<String> responseDTO = getDingTalkAccessToken();
        if (!responseDTO.isSuccess()) {
            return ResponseDTO.fail(responseDTO.getMessage());
        }
        String accessToken = responseDTO.getData();
        DingTalkClient client = new DefaultDingTalkClient(DingTalkConstant.USER_ID_BY_DETAILED_URL);
        OapiV2UserGetRequest req = new OapiV2UserGetRequest();
        req.setUserid(userId);
        req.setLanguage(DingTalkConstant.LANGUAGE);
        try {
            OapiV2UserGetResponse rsp = client.execute(req, accessToken);
            log.info("[{}], {}, 钉钉获取用户：{}，详细信息:{}", TAG, uuid, userId, rsp.getBody());
            if (rsp.getErrcode() == 0) {
                JSONObject body = JSON.parseObject(rsp.getBody());
                JSONObject result = body.getJSONObject("result");
                DingTalkUserDetailedInfo information = JSONObject.parseObject(result.toJSONString(), DingTalkUserDetailedInfo.class);
                return ResponseDTO.success(information);
            } else {
                return ResponseDTO.fail(rsp.getMessage());
            }
        } catch (ApiException e) {
            log.error("[{}], {}, 钉钉获取用户详细信息异常:{}", TAG, uuid, e.getErrMsg(), e);
            return ResponseDTO.fail("钉钉获取用户详细信息失败！");
        }
    }

    /**
     * 设置UserInfo
     * @param userInfo
     * @param dio
     * @return
     */
    private UserInfo setUserInfo(DingTalkUserInfo userInfo,UserAuthLoginDIO dio){
        return UserInfo.builder()
                .userId(null)
                .openId(userInfo.getOpenId())
                .unionId(userInfo.getUnionId())
                .type(userInfo.getMainOrgAuthHighLevel() ? 2 : 1)
                .realname(StringUtil.isBlank(userInfo.getNick()) ? "钉钉用户" : userInfo.getNick())
                .username(null)
                .nickName(userInfo.getNick())
                .avatarSrc(userInfo.getAvatar())
                .age(null)
                .gender(dio.getSex() == null ? 3 : dio.getSex())
                .country(dio.getCountry())
                .province(dio.getProvince())
                .city(dio.getCity())
                .build();
    }

    /**
     * 设置PlatInfo
     * @param dio
     * @param app
     * @param channelId
     * @return
     */
    private PlatInfo setPlatInfo(UserAuthLoginDIO dio,AppDRO app,Integer channelId){
        return PlatInfo.builder()
                .shopId(dio.getShopId())
                .plat(app.getPlat())
                .channelId(channelId)
                .channelName(getNameByChannelId(channelId))
                .entrance(dio.getEntrance())
                .build();
    }

    /**
     * 设置ThirdAppInfo
     * @param app
     * @return
     */
    private ThirdAppInfo setThirdAppInfo(AppDRO app){
        return ThirdAppInfo.builder()
                .appId(app.getThirdAppId())
                .appName(app.getName())
                .appSecret(suiteSecret)
                .build();
    }

    /**
     * 设置UserAuthLoginInfo
     * @param info
     * @param platInfo
     * @param thirdAppInfo
     * @param authPhoneInfo
     * @return
     */
    private UserAuthLoginInfo setUserAuthLoginInfo(UserInfo info,PlatInfo platInfo,ThirdAppInfo thirdAppInfo,UserAuthPhoneInfo authPhoneInfo){
        return UserAuthLoginInfo.builder()
                .userInfo(info)
                .userOtherInfo(new UserOtherInfo())
                .platInfo(platInfo)
                .thirdAppInfo(thirdAppInfo)
                .userPhoneInfo(UserPhoneInfo.builder().mobile(authPhoneInfo.getMobile()).build())
                .build();
    }
}
